﻿Imports System.Windows.Forms
Imports System.IO
Imports System.Text.RegularExpressions
Imports System.ComponentModel
Imports System.Drawing

''' <summary>
''' 本地计算机目录树
''' Author : ajayumi
''' Date   : 2010-10-03
''' </summary>
''' <remarks></remarks>
Public Class DirectoryTreeView
    Inherits TreeView

#Region "variables"

    Private m_PrevSelectionNode As TreeNode = Nothing '上一次选择的节点

#End Region

#Region "properties"

    ''' <summary>
    ''' 获取当前选择的目录
    ''' </summary>
    Public ReadOnly Property DirectoryName() As String
        Get
            Dim _Node As TreeNode = Me.SelectedNode
            Return Me.ConvertToPath(_Node)
        End Get
    End Property

#End Region

    Public Sub New()

    End Sub

#Region "events"

    ''' <summary>
    ''' 开始加载节点事件
    ''' </summary>
    ''' <remarks></remarks>
    Public Event BeginLoad As EventHandler
    Protected Overridable Sub OnBeginLoad(ByVal e As EventArgs)
        RaiseEvent BeginLoad(Me, e)
    End Sub

    ''' <summary>
    ''' 加载节点完毕事件
    ''' </summary>
    ''' <remarks></remarks>
    Public Event EndLoad As AsyncCompletedEventHandler
    Protected Overridable Sub OnEndLoad(ByVal e As AsyncCompletedEventArgs)
        RaiseEvent EndLoad(Me, e)
    End Sub

    Protected Overrides Sub OnBeforeExpand(ByVal e As System.Windows.Forms.TreeViewCancelEventArgs)
        MyBase.OnBeforeExpand(e)

        For Each item As TreeNode In e.Node.Nodes
            Me.Invoke(New Action(AddressOf item.Nodes.Clear))

            Dim _ParentDirectory As String = Regex.Match(item.FullPath, "[^\\]+:\\.*", RegexOptions.IgnoreCase).Value
            Dim _Action As New Action(Of TreeNode, String)(AddressOf Me.getDirectoryList)
            _Action.BeginInvoke(item, _ParentDirectory, New AsyncCallback(AddressOf Me.getDirectoryListCallback), _Action)
        Next
    End Sub

    Protected Overrides Sub OnAfterCollapse(ByVal e As System.Windows.Forms.TreeViewEventArgs)
        MyBase.OnAfterCollapse(e)
        e.Node.Collapse()
    End Sub

    Protected Overrides Sub OnNodeMouseClick(ByVal e As System.Windows.Forms.TreeNodeMouseClickEventArgs)
        MyBase.OnNodeMouseClick(e)

        Me.markSelectionNode(e.Node)
    End Sub

#End Region



#Region "methods"

    ''' <summary>
    ''' 标志选择的节点
    ''' </summary>
    ''' <param name="node"></param>
    ''' <remarks></remarks>
    Private Sub markSelectionNode(ByVal node As TreeNode)
        If Me.m_PrevSelectionNode IsNot Nothing Then
            Me.m_PrevSelectionNode.BackColor = Me.BackColor
        End If
        node.BackColor = Color.FromArgb(204, 204, 204)
        Me.m_PrevSelectionNode = node
    End Sub

    Private Sub getDirectoryList(ByVal node As TreeNode, ByVal dir As String)
        Me.OnBeginLoad(Nothing)
        Dim _Directories() As String = Me.GetDirectory(dir)
        Me.FillNodes(node, Me.GenerateNodes(_Directories))
    End Sub

    Private Sub getDirectoryListCallback(ByVal ar As IAsyncResult)
        Dim _Action As Action(Of TreeNode, String) = DirectCast(ar.AsyncState, Action(Of TreeNode, String))
        _Action.EndInvoke(ar)
        Me.OnEndLoad(New AsyncCompletedEventArgs(Nothing, False, Me))
    End Sub

    Private Function GenerateNodes(ByVal strs() As String) As TreeNode()
        Dim _Len As Integer = strs.Length
        Dim _Nodes(_Len - 1) As TreeNode
        For i As Integer = 0 To _Len - 1
            Dim _Text As String = strs(i)
            If _Text.EndsWith(":\") Then
            Else
                _Text = Regex.Match(_Text, "[^\\]+$").Value
            End If
            _Nodes(i) = New TreeNode() With {.Text = _Text}
        Next
        Return _Nodes
    End Function

    ''' <summary>
    ''' 填充节点
    ''' </summary>
    ''' <param name="parentNode"></param>
    ''' <param name="childNodes"></param>
    Private Sub FillNodes(ByVal parentNode As TreeNode, ByVal childNodes() As TreeNode)
        Me.BeginInvoke(New Action(Of TreeNode())(AddressOf parentNode.Nodes.AddRange), New Object() {childNodes})
    End Sub

    Private Function GetDirectory(ByVal parentDir As String) As String()
        If String.IsNullOrEmpty(parentDir) Then
            Return Directory.GetLogicalDrives()
        Else
            Try
                Return Directory.GetDirectories(parentDir)
            Catch ex As Exception
                Return New String() {}
            End Try
        End If
    End Function



    ''' <summary>
    ''' 异步加载节点
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub AsyncLoad()
        Dim _Node As New TreeNode() With {.Text = "我的电脑"}
        Me.BeginInvoke(New Action(Of TreeNode())(AddressOf Me.Nodes.AddRange), New Object() {New TreeNode() {_Node}})

        Dim _Action As New Action(Of TreeNode, String)(AddressOf Me.getDirectoryList)
        _Action.BeginInvoke(_Node, String.Empty, New AsyncCallback(AddressOf Me.getDirectoryListCallback), _Action)
    End Sub

    Public Overrides Sub Refresh()
        MyBase.Refresh()
        Me.AsyncLoad()
    End Sub

    ''' <summary>
    ''' 转换为目录路径
    ''' </summary>
    ''' <param name="node"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function ConvertToPath(ByVal node As TreeNode) As String
        Dim _Directory As String = String.Empty
        If node IsNot Nothing Then
            _Directory = Regex.Match(node.FullPath, "[^\\]+:\\.*", RegexOptions.IgnoreCase).Value
            _Directory = _Directory.Replace("\\", "\")
        End If
        Return _Directory
    End Function

#End Region

End Class
